# coding=utf-8
import json
from fnmatch import fnmatch

from elasticsearch_dsl import Document, Boolean, analyzer, Keyword, Text, Integer, Date, tokenizer, GeoPoint

import settings
from social.user.models import AVCallSetting
from social.utils.shortcut import get_object_or_none

search_initial_timestamp = 1546272000  # 2019-01-01

one_gram_analyzer = analyzer('one_gram_analyzer',
                             tokenizer=tokenizer('one_gram', 'nGram', token_chars=['letter', 'digit']),
                             filter=['lowercase', 'standard'])


class BaseDocument(Document):

    @classmethod
    def _matches(cls, hit):
        return fnmatch(hit['_index'], cls._index._name + "-v*")

    @classmethod
    def get_version(cls):
        return 1


class EsUser(BaseDocument):
    gender = Keyword()
    last_active = Date()
    location = GeoPoint()
    # 是否真实头像
    avatar_real = Boolean()
    # 真人认证
    avatar_auth = Boolean()
    # 角色
    role = Keyword()
    # 是否可以通话
    call_enabled = Boolean()
    # 总消费
    total_consume = Integer()

    @classmethod
    def update_user(cls, user):
        if user.is_admin:
            return
        fields = ['gender', 'last_active', 'avatar_real', 'role', 'avatar_auth', 'created_at']
        save_kwargs = {}
        for i in fields:
            save_kwargs[i] = getattr(user, i)
        if user.avatar_auth:
            call_setting, _ = AVCallSetting.objects.get_or_create(user=user)
            save_kwargs['call_enabled'] = call_setting.call_enabled
            save_kwargs['call_level'] = call_setting.setting_level
        if user.lat and user.lng:
            save_kwargs['location'] = {'lat': user.lat, 'lon': user.lng}
        else:
            save_kwargs['location'] = {'lat': 0, 'lon': 0}

        return cls(meta={'id': user.id}, **save_kwargs).save()

    @classmethod
    def related_user_fields(cls):
        # 以下字段变动时才需要更新ES
        return ['gender', 'created_at', 'avatar_auth', 'last_active', 'lat', 'lng', 'role']

    @classmethod
    def get_version(cls):
        return 1

    class Index:
        name = f"{settings.APP_NAME}_test_user" if settings.DEBUG else f'{settings.APP_NAME}_user'


class EsPost(BaseDocument):
    user_id = Integer()
    gender = Keyword()
    like_count = Integer()
    comment_count = Integer()
    see_count = Integer()
    label = Keyword()

    @classmethod
    def new_instance(cls, post):
        post.refresh_from_db()
        save_kwargs = {'user_id': post.user.id,
                       'like_count': post.like_count, 'comment_count': post.comment_count,
                       'see_count': post.see_count, 'gender': post.user.gender}
        return cls(meta={'id': post.id}, **save_kwargs).save()

    @classmethod
    def related_user_fields(cls):
        # 以下字段变动时才需要更新ES
        return ['gender', 'like_count', 'comment_count', 'see_count', 'label']

    @classmethod
    def get_version(cls):
        return 1

    class Index:
        name = f"{settings.APP_NAME}_test_post" if settings.DEBUG else f'{settings.APP_NAME}_post'


def setup():
    for i in [EsUser, EsPost]:
        if not i._index.exists():
            migrate(model=i, move_data=False)


def migrate(model, move_data=True, update_alias=True):
    """
    Upgrade function that creates a new index for the data. Optionally it also can
    (and by default will) reindex previous copy of the data into the new index
    (specify ``move_data=False`` to skip this step) and update the alias to
    point to the latest index (set ``update_alias=False`` to skip).
    Note that while this function is running the application can still perform
    any and all searches without any loss of functionality. It should, however,
    not perform any writes at this time as those might be lost.
    """

    if not (move_data or update_alias):
        return

    index_name = model._index._name
    version = model.get_version()
    next_index = f"{index_name}-v{version}"

    assert version > 0, f"version error:{version}"

    es = settings.ES
    model.init(index=next_index)

    if move_data:
        # move data from current alias to the new index
        es.reindex(
            body={"source": {"index": index_name}, "dest": {"index": next_index}},
            request_timeout=3600
        )
        # refresh the index to make the changes visible
        es.indices.refresh(index=next_index)

    if update_alias:
        # repoint the alias to point to the newly created index
        es.indices.update_aliases(body={
            'actions': [
                {"remove": {"alias": index_name, "index": f"{index_name}-v*"}},
                {"add": {"alias": index_name, "index": next_index}},
            ]
        })
